// synch.cc 
//      Routines for synchronizing threads.  Three kinds of
//      synchronization routines are defined here: semaphores, locks 
//      and condition variables (the implementation of the last two
//      are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabledthreadtest (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      Initialize a semaphore, so that it can be used for synchronization.
//
//      "debugName" is an arbitrary name, useful for debugging.
//      "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore (char *debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
//      De-allocate semaphore, when no longer needed.  Assume no one
//      is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore ()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
//      Wait until semaphore value > 0, then decrement.  Checking the
//      value and decrementing must be done atomically, so we
//      need to disable interrupts before checking the value.
//
//      Note that Thread::Sleep assumes that interrupts are disabled
//      when it is called.
//----------------------------------------------------------------------

void
Semaphore::P ()
{
    IntStatus oldLevel = interrupt->SetLevel (IntOff);	// disable interrupts

    while (value == 0)
      {				// semaphore not available
	  queue->Append ((void *) currentThread);	// so go to sleep
	  currentThread->Sleep ();
      }
    value--;			// semaphore available,     // consume its value

    (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
//      Increment semaphore value, waking up a waiter if necessary.
//      As with P(), this operation must be atomic, so we need to disable
//      interrupts.  Scheduler::ReadyToRun() assumes that threads
//      are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V ()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel (IntOff);

    thread = (Thread *) queue->Remove ();
    if (thread != NULL)		// make thread ready, consuming the V immediately
	scheduler->ReadyToRun (thread);
    value++;
    (void) interrupt->SetLevel (oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!



//----------------------------------------------------------------------
// Lock::Lock
//      Create a new lock which is free and is associated with a queue     
//----------------------------------------------------------------------
Lock::Lock (char *debugName)
{
  name = debugName;
  value = FREE;
  owner = NULL;
  queue = new List;
}

//----------------------------------------------------------------------
// Lock::~Lock
//      Deallocate the lock, delete his queue    
//----------------------------------------------------------------------
Lock::~Lock ()
{
  delete queue;
}

//----------------------------------------------------------------------
// Lock::Acquire
//      Attempt to acquire the lock
//      If the lock is not free, the current thread is enqueued and fall
//      asleep
//----------------------------------------------------------------------
void
Lock::Acquire ()
{

  IntStatus oldLevel = interrupt->SetLevel (IntOff); // disable interrupts

  while ( value != FREE ){
      queue->Append ((void *) currentThread);  // Save the thread in the list
      currentThread->Sleep ();                 // yield the current thread
  }

  value = BUSY;
  owner = currentThread;

(void) interrupt->SetLevel (oldLevel);	// re-enable interrupts
  
}
//----------------------------------------------------------------------
// Lock::Release
//      Release the lock by changing his status (set at free) and
//      the thread is dequeued in the list of waiting threads 
//----------------------------------------------------------------------
void
Lock::Release ()
{
  ASSERT (isHeldByCurrentThread()); 
  
  Thread *thread;
  IntStatus oldLevel = interrupt->SetLevel (IntOff); // disable interrupts
  value = FREE;
  
  thread = (Thread *) queue->Remove();

  if(thread != NULL)
    scheduler->ReadyToRun (thread);
  
  (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts
    
}

//----------------------------------------------------------------------
// Lock::isHeldByCurrentThread
//      Test is the lock is held by the current thread
//----------------------------------------------------------------------
bool
Lock::isHeldByCurrentThread ()
{
  return ( currentThread == owner );
} 


//----------------------------------------------------------------------
// Condition::Condition
//      Create a new condition and allocate a list of waiting threads
//----------------------------------------------------------------------
Condition::Condition (char *debugName)
{
  waitingThreads = new List();
}

//----------------------------------------------------------------------
// Condition::~Condition
//      Deallocate the list of the waiting threads
//----------------------------------------------------------------------
Condition::~Condition ()
{
  delete waitingThreads;
}

//----------------------------------------------------------------------
// Condition::Wait
//      If the current thread is holding the condition lock, he releases
//      the lock, he is enqueued in the list of the waiting threads and
//      finally falls asleep
//----------------------------------------------------------------------
void
Condition::Wait (Lock * conditionLock)
{

  ASSERT(conditionLock->isHeldByCurrentThread());

  IntStatus oldLevel = interrupt->SetLevel (IntOff); // disable interrupts

  conditionLock->Release();              // Release the lock

  waitingThreads->Prepend(currentThread); // Add the current thread in the list of 
                                         // the waiting threads

 

  currentThread->Sleep();                        // Put the thread to sleep

  (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts

  conditionLock->Acquire();                     // Acquire the lock
}

//----------------------------------------------------------------------
// Condition::Signal
//      Send a signal to the first thread in the waiting list. This thread
//      is put in the scheduler
//----------------------------------------------------------------------
void
Condition::Signal (Lock * conditionLock)
{
  ASSERT(conditionLock->isHeldByCurrentThread());

  Thread *first_waiting_thread;

  if(!waitingThreads->IsEmpty()){

    IntStatus oldLevel = interrupt->SetLevel (IntOff); // disable interrupts

    // The first waiting thread becomes the current thread
    // Remove the current thread from the list     
    first_waiting_thread = (Thread*)waitingThreads->Remove();
    
    // Put the thread in the scheduler
    scheduler->ReadyToRun (first_waiting_thread);

    (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts

  }
}

//----------------------------------------------------------------------
// Condition::Broadcast
//      Send a signal to the entire list of the waiting threads
//----------------------------------------------------------------------
void
Condition::Broadcast (Lock * conditionLock)
{
  ASSERT(conditionLock->isHeldByCurrentThread());
  
  Thread *first_waiting_thread;

  IntStatus oldLevel = interrupt->SetLevel (IntOff); // disable interrupts

  while(!waitingThreads->IsEmpty()){
    

    // The first waiting thread becomes the current thread
    // Remove the current thread from the list     
    first_waiting_thread = (Thread*)waitingThreads->Remove();
    
    // Put the thread in the scheduler
    scheduler->ReadyToRun (first_waiting_thread);

  }
    (void) interrupt->SetLevel (oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Communicator::Communicator
//      Initialize a Communicator
//      - channel : 1 if the channel is used, else 0
//      - msg_sent: TRUE if a message have been sent, else FALSE 
//----------------------------------------------------------------------
Communicator::Communicator(char *DebugName){
  name = DebugName;

  lockcom = new Lock("LockCom");

  cond_speak = new Condition("Cond_Speak");
  cond_listen = new Condition("Cond_Listen");

  msg=-1;


  // Flags : to know if we are speaking or listening
  channel = 1;
  msg_sent = FALSE;
}

//----------------------------------------------------------------------
// Communicator::Communicator
//      De-allocate the Communicator
//----------------------------------------------------------------------
Communicator::~Communicator(){
  delete lockcom;
  delete cond_speak;
  delete cond_listen;
}

//----------------------------------------------------------------------
// Communicator::Speak
//      
//----------------------------------------------------------------------
void
Communicator::Speak(int word){
  
  lockcom->Acquire();              // Acquire the communicator lock

  while(channel != 1){             // If a thread is already sending a message
                                   // the thread is waiting
    cond_speak->Wait(lockcom);
  }
  channel=0;                       // No one else can sen a message

  msg=word;                        // Send the Message

  msg_sent=TRUE;                   // the message is sent, someone can listen

  cond_listen->Signal(lockcom);    // Signal to wake up a thread who waits

  lockcom->Release();              // Release the communicator lock
}

//----------------------------------------------------------------------
// Communicator::Listen
//      If the channel is used to send message, the thread listens 
//      and return the value
//----------------------------------------------------------------------
int
Communicator::Listen(){
  
  lockcom->Acquire();            // Acquire the communication lock
  
  while(msg_sent==FALSE){        // If a message is already sent, the thread waits
    cond_listen->Wait(lockcom);
  }
  
  msg_sent=FALSE;                // No one else can listen
  tmp=msg;                       // Store the message in a temporary variable
  channel=TRUE;                  // The channel is free

  cond_speak->Signal(lockcom);   // Send a signal to wake up a sleeping thread
  lockcom->Release();            // Release the lock
  return tmp;                    // End of the function by returning tmp
}
